#ifndef __MQ_BINDING_HPP__
#define __MQ_BINDING_HPP__

#include "mq_exchange.hpp"
#include "mq_queue.hpp"
#include <iostream>
#include <string>
#include <unordered_map>
#include "../mqcommon/mq_helper.hpp"
#include <cassert>
#include <memory>
#include <mutex>

namespace mq
{
    struct mqbinding
    {
        using Ptr = std::shared_ptr<mqbinding>;
        // 交换机名称
        std::string exchange_name;
        // 队列名称
        std::string queue_name;
        // 句柄
        std::string bind_key;

        mqbinding() {}
        mqbinding(const std::string& mqexchange_name,
            const std::string& mqqueue_name,
            const std::string& mqbind_key
            )
            :exchange_name(mqexchange_name)
            ,queue_name(mqqueue_name)
            ,bind_key(mqbind_key)
        {}
    };
    using MsgQueueBindingMap = std::unordered_map<std::string, mqbinding::Ptr>;
    using BindingMap = std::unordered_map<std::string, MsgQueueBindingMap>;

    class BindMapper
    {
    public:
        BindMapper(const std::string& dbfile)
            :_sql_helper(dbfile)
        {
            std::string parentPath = mq::Helper::FileHelper::parentDirctory(dbfile);
            mq::Helper::FileHelper::createDirectory(parentPath);
            assert(_sql_helper.open());
            createTable();
        }
        void createTable()
        {
            std::stringstream ss;
            ss << "create table if not exists bind_table(";
            // 不需要表明唯一性就不用在后面加上primary key。
            // ss << "exchange_name varchar(32) primary key,";
            ss << "exchange_name varchar(32),";
            ss << "queue_name varchar(32),";
            ss << "bind_key varchar(32));";
            bool ret = _sql_helper.exec(ss.str(),nullptr,nullptr);
            if(ret == false)
            {
                DLOG("创建bind_table失败。");
                abort();
            }
        }
        void removeTable()
        {
            std::stringstream ss;
            ss << "drop table if exists bind_table;";
            bool ret = _sql_helper.exec(ss.str(),nullptr,nullptr);
            if(ret == false)
            {
                DLOG("删除bind_table失败。");
                abort();
            }
        }
        bool insert(mqbinding::Ptr mbp)
        {
            std::stringstream ss;
            ss << "insert into bind_table values(";
            ss << "'" << mbp->exchange_name << "',";
            ss << "'" << mbp->queue_name << "',";
            ss << "'" << mbp->bind_key << "');";

            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }
        bool remove(const std::string& ename,const std::string& qname)
        {
            std::stringstream ss;
            ss << "delete bind_table where ";
            ss << "'exchange_name=" << ename << "'" ;
            ss << " and ";
            ss << "'queue_name=" << qname << "';";
            
            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }
        bool removeExchangeBindings(const std::string &ename)
        {
            // 删除了交换机，那他的信息也没有必要保留了。
            std::stringstream ss;
            ss << "delete bind_table where";
            ss << " 'exchange_name'=" << ename << "';";
            
            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }
        bool removeQueueBindings(const std::string &qname)
        {
            std::stringstream ss;
            ss << "delete bind_table where name=";
            ss << " 'queue_name'=" << qname << ";";

            return _sql_helper.exec(ss.str(),nullptr,nullptr);
        }
        BindingMap recovery()
        {
            BindingMap result;
            std::string sql = "select exchange_name,queue_name,bind_key from bind_table;";
            _sql_helper.exec(sql,&BindMapper::selectCallback,&result);
            return result;
        }
    private:
        // static记得要加，不然不能从外部调用函数。
        static int selectCallback(void* arg,int numcol,char** row,char** fields)
        {
            BindingMap* result = (BindingMap*)arg;
            // 都是字符串无需特殊化处理。
            mqbinding::Ptr bmp = std::make_shared<mqbinding>(row[0],row[1],row[2]);
            MsgQueueBindingMap& qmap = (*result)[bmp->exchange_name];
            qmap.insert(std::make_pair(bmp->queue_name, bmp));

            return 0;
        }
    private:
        mq::Helper::SqliteHelper _sql_helper;
    };
    class BindManager
    {
    public:
        using Ptr = std::shared_ptr<BindManager>;
        BindManager(const std::string& dbfile)
            :_mapper(dbfile)
        {
            // 恢复数据。
            // 这样就不会插入重复的值。
            _binds = _mapper.recovery();
        }
        bool bind(const std::string ename,const std::string qname,const std::string key,bool durable)
        {
            std::unique_lock<std::mutex> lock(_mutex);
            
            auto bit = _binds.find(ename);
            // 不存在则创建 , 存在则返回。
            // 由于创建相对麻烦，所以我们让存在先返回。
            if(bit != _binds.end() && bit->second.find(qname) != bit->second.end())
            {
                return true;
            }
            mqbinding::Ptr mbp = std::make_shared<mqbinding>(ename,qname,key);
            if(durable == true)
            {
                bool ret = _mapper.insert(mbp);
                if(ret == false)
                {
                    return false;
                }
            }
            // 队列不存在，插入后返回。
            if(bit != _binds.end() && bit->second.find(qname) == bit->second.end())
            {
                bit->second.insert(std::make_pair(qname,mbp));
                return true;
            }

            auto &qbmap = _binds[ename]; 
            qbmap.insert(std::make_pair(qname, mbp)); 
            
            return true;

            // MsgQueueBindingMap queues;
            // queues.insert(std::make_pair(qname,mbp));
            // _binds.insert(std::make_pair(ename,queues));

            // return true;
        }
        bool unbind(const std::string& ename,const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // mapper不管三期二十一就是删除
            auto bit = _binds.find(ename);
            // 没有交换机。
            if(bit == _binds.end())
            {
                return true;
            }

            auto qit = bit->second.find(qname);
            // 没有要删除的队列
            if(qit == bit->second.end())
            {
                return true;
            }
            _mapper.remove(ename,qname);
            bit->second.erase(qit);
            return true;
        }
        void removeExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // 由于_mapper需要查找。
            // 所以我们需要迭代器。
            _mapper.removeExchangeBindings(ename);
            _binds.erase(ename);

            return ;
        }
        void removeQueueBindings(const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // 先找到该消息队列。
            for(auto bit = _binds.begin();bit != _binds.end();++bit)
            {
                bit->second.erase(qname);
            }
            _mapper.removeQueueBindings(qname);

            return ;
        }
        MsgQueueBindingMap getExchangeBindings(const std::string& ename)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            // 找到交换机的队列。
            auto bit = _binds.find(ename);
            if(bit == _binds.end())
            {
                DLOG("不存在交换机:%s",ename.c_str());

                return MsgQueueBindingMap();
            }

            return bit->second;
        }
        mqbinding::Ptr getBinding(const std::string &ename, const std::string &qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto bit = _binds.find(ename);

            if(bit != _binds.end())
            {
                auto& qit = bit->second;
                auto ret = qit.find(qname);
                if(ret != qit.end())
                {
                    return ret->second;
                }
            }

            return mq::mqbinding::Ptr();
        }
        bool exists(const std::string& ename, const std::string& qname)
        {
            std::unique_lock<std::mutex> lock(_mutex);

            auto bit = _binds.find(ename);
            if(bit == _binds.end())
            {
                return false;
            }
            auto qit = bit->second.find(qname);
            if(qit == bit->second.end())
            {
                return false;
            }

            return true;
        }
        size_t size()
        {
            std::unique_lock<std::mutex> lock(_mutex);
            size_t total = 0;
            for(auto start = _binds.begin();start != _binds.end();++start)
            {
                total += start->second.size();
            }

            return total;
        }
        void clear()
        {
            std::unique_lock<std::mutex> lock(_mutex);

            _mapper.removeTable();
            _binds.clear();

            return ;
        }
    private:
        std::mutex _mutex;
        BindMapper _mapper;
        BindingMap _binds;
    };
}

#endif